//
// Created by DELL on 2021/3/10.
//

#ifndef SYLAR_SCHEDULER_H
#define SYLAR_SCHEDULER_H

#include <memory>
#include <vector>
#include <list>
#include <iostream>
#include "fiber.h"
#include "thread.h"

namespace sylar {
    class Scheduler {
    public:
        using ptr = std::shared_ptr<Scheduler>;
        using MutexType = Mutex;

        //是否使用当前调用线程
        explicit Scheduler(size_t threads = 1, bool use_caller = true, const std::string &name = "");

        virtual ~Scheduler();

        const std::string &getName() const { return m_name; }

        static Scheduler *GetThis();

        static Fiber *GetMainFiber();

        void start();

        void stop();

        template<class FiberOrCb>
        void schedule(FiberOrCb fc, int thread = -1) {
            bool need_tickle = false;
            {
                MutexType::Lock lock(m_mutex);
                need_tickle = scheduleNoLock(fc, thread);
            }
            if (need_tickle) {
                tickle();
            }
        }

        template<class InputInterator>
        void schedule(InputInterator begin, InputInterator end) {
            bool need_tickle = false;
            {
                MutexType::Lock lock(m_mutex);
                while (begin != end) {
                    need_tickle = scheduleNoLock(&*begin, -1) || need_tickle;
                    ++begin;
                }
            }
            if (need_tickle) {
                tickle();
            }
        }

        void switchTo(int thread = -1);

        std::ostream &dump(std::ostream &os);

    protected:
        virtual void tickle();

        virtual bool stopping();

        void run();

        virtual void idle();

        void setThis();

        bool hasIdleThreads() { return m_idleThreadCount > 0; }

    private:
        template<class FiberOrCb>
        bool scheduleNoLock(FiberOrCb fc, int thread) {
            bool need_tickle = m_fibers.empty();
            FiberAndThread ft(fc, thread);
            if (ft.fiber || ft.cb) {
                m_fibers.push_back(ft);
            }
            return need_tickle;
        }

    private:
        struct FiberAndThread {
            Fiber::ptr fiber;
            std::function<void()> cb;
            int thread;

            FiberAndThread(Fiber::ptr f, int thr)
                    : fiber(f),
                      thread(thr) {
            }

            FiberAndThread(Fiber::ptr *f, int thr)
                    : thread(thr) {
                fiber.swap(*f);
            }

            FiberAndThread(std::function<void()> f, int thr)
                    : cb(f), thread(thr) {
            }

            FiberAndThread(std::function<void()> *f, int thr)
                    : thread(thr) {
                cb.swap(*f);
            }

            FiberAndThread()
                    : thread(-1) {
            }

            void reset() {
                fiber = nullptr;
                cb = nullptr;
                thread = -1;
            }
        };

    private:
        MutexType m_mutex;
        // 线程池
        std::vector<Thread::ptr> m_threads;
        // 待执行的协程队列
        std::list<FiberAndThread> m_fibers;
        Fiber::ptr m_rootFiber;
        // 协程调度器的名字
        std::string m_name;
    protected:
        /// 协程下的线程id数组
        std::vector<int> m_threadIds;
        /// 线程数量
        size_t m_threadCount = 0;
        /// 工作线程数量
        std::atomic<size_t> m_activeThreadCount = {0};
        /// 空闲线程数量
        std::atomic<size_t> m_idleThreadCount = {0};
        /// 是否正在停止
        bool m_stopping = true;
        /// 是否自动停止
        bool m_autoStop = false;
        /// 主线程id(use_caller)
        int m_rootThread = 0;
    };

    class SchedulerSwitcher : public Noncopyable {
    public:
        explicit SchedulerSwitcher(Scheduler *target = nullptr);

        ~SchedulerSwitcher();

    private:
        Scheduler *m_caller;
    };
}
#endif //SYLAR_SCHEDULER_H
